# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Tests keyboard pin connectivity in SMT factory test.

Unlike keyboard test, it only expects a key sequence where keys are the keyboard
scan lines' row-column crossing points. It also can trigger a SMT testing
fixture to send out signals to simulate key presses on the key sequence.
"""

from __future__ import print_function
import evdev
import unittest

import factory_common  # pylint: disable=W0611
from cros.factory.test import evdev_utils
from cros.factory.test import factory
from cros.factory.test import test_ui
from cros.factory.test.args import Arg
from cros.factory.test.countdown_timer import StartCountdownTimer
from cros.factory.test.fixture.bft_fixture import (CreateBFTFixture,
                                                   TEST_ARG_HELP)
from cros.factory.test.ui_templates import OneSection
from cros.factory.test.utils import StartDaemonThread


_ID_CONTAINER = 'keyboard-test-container'
_ID_EXPECTED_SEQUENCE = 'expected-sequence'
_ID_MATCHED_SEQUENCE = 'matched-sequence'
_ID_COUNTDOWN_TIMER = 'keyboard-test-timer'

_MSG_EXPECTED_SEQUENCE = test_ui.MakeLabel(
    'Expected keycode sequence:', u'目標鍵序：', 'test-info')

_HTML_KEYBOARD = '<br>\n'.join([
    '<div>%s <span id="%s"></span><span id="%s"></span></div>' % (
        _MSG_EXPECTED_SEQUENCE, _ID_MATCHED_SEQUENCE, _ID_EXPECTED_SEQUENCE),
    '<div id="%s" class="test-info"></div>' % _ID_COUNTDOWN_TIMER])

_KEYBOARD_TEST_DEFAULT_CSS = (
    '.test-info { font-size: 1.4em; }\n'
    '#expected-sequence { color: grey; font-size: 1.4em }\n'
    '#matched-sequence { color: black; font-size: 1.4em }\n')


class KeyboardSMTTest(unittest.TestCase):
  """Tests each keyboard scan lines are connected.

  It triggers akeyboard scan module by sending 0xC1 to fixture via RS-232.
  The keyboard scan module will send a sequence of keycodes. This test checks
  if the upcoming keyup events matche the expected keycode sequence.
  """
  ARGS = [
      Arg(
          'keyboard_event_id', int, 'Keyboard input event id.', default=None,
          optional=True),
      Arg('timeout_secs', int, 'Timeout for the test.', default=30),
      Arg(
          'keycode_sequence', tuple,
          'Expected keycode sequence generated by a keyboard scan module in '
          'the fixture.'),
      Arg('bft_fixture', dict, TEST_ARG_HELP, optional=True),
      Arg(
          'debug', bool,
          'True to disable timeout and never fail. Used to observe keystrokes.',
          default=False)]

  def setUp(self):
    self.ui = test_ui.UI()
    self.template = OneSection(self.ui)
    self.ui.AppendCSS(_KEYBOARD_TEST_DEFAULT_CSS)

    # Initialize frontend presentation.
    self.template.SetState(_HTML_KEYBOARD)
    self.ui.CallJSFunction('setUpKeyboardTest', self.args.keycode_sequence,
                           self.args.debug)

    self.fixture = None
    if self.args.bft_fixture:
      self.fixture = CreateBFTFixture(**self.args.bft_fixture)

    # Get the keyboard input device.
    if self.args.keyboard_event_id is None:
      keyboard_devices = evdev_utils.GetKeyboardDevices()
      assert len(keyboard_devices) == 1, 'Multiple keyboards detected.'
      self.event_dev = keyboard_devices[0]
    else:
      self.event_dev = evdev.InputDevice(
          '/dev/input/event%d' % self.args.keyboard_event_id)

    # Monitor keyboard event within specified time period.
    self.event_dev.grab()
    StartDaemonThread(target=self.PollEvdevEvent)
    if not self.args.debug:
      StartCountdownTimer(self.args.timeout_secs, self.TimeoutHandler, self.ui,
                          _ID_COUNTDOWN_TIMER)

  def tearDown(self):
    self.event_dev.ungrab()

  def TimeoutHandler(self):
    """Called to fail the test when a timeout is reached."""
    self.ui.CallJSFunction(
        'failTest',
        'Timeout after %d seconds.' % self.args.timeout_secs)

  def PollEvdevEvent(self):
    """Polls evdev event."""
    for event in self.event_dev.read_loop():
      self.HandleEvdevEvent(event)

  def HandleEvdevEvent(self, event):
    """Handles evdev event.

    Notifies JS if a keyup event is received.

    Args:
      event: evdev event.
    """
    if event.type == evdev.ecodes.EV_KEY and event.value == 0:
      if self.args.debug:
        factory.console.info('keycode: %s' % event.code)
      self.ui.CallJSFunction('markKeyup', event.code)

  def runTest(self):
    if self.fixture:
      self.fixture.SimulateKeystrokes()
    self.ui.Run()
